#include <milfs_keyvalue.h>
#include <milfs_object.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <time.h>


mlf_object *mlf_object_new(uint8_t type)
{
  mlf_object *object = NULL;

  object = (mlf_object *) malloc(sizeof(mlf_object));

  object->type = type;
  object->attributes = NULL;

  return object;
}

void mlf_object_push(mlf_object *object, char *key, char *value, int vsize)
{
  __mlf_kv_list *iter, *old, *new;

  old = NULL;
  iter = object->attributes;

  while ( iter != NULL ) {
    if ( iter->entry->key_size == strlen(key) ) {
      if ( strncmp(iter->entry->key, key, iter->entry->key_size) == 0 ) {
	old = iter;
	break;
      } else {
	iter = (__mlf_kv_list *) iter->next;
      }
    } else {
      iter = (__mlf_kv_list *) iter->next;
    }
  }

  if ( old == NULL ) {
    new = (__mlf_kv_list *) malloc( sizeof(__mlf_kv_list) );
    new->entry = (mlf_key_value *) malloc ( sizeof(mlf_key_value) );
    /* Setup the key */
    new->entry->key_size = sizeof(char) * strlen(key);
    new->entry->key = malloc( new->entry->key_size );
    strncpy(new->entry->key, key, new->entry->key_size);
    /* setup the value */
    new->entry->value_size = vsize;
    new->entry->value = malloc( vsize );
    memcpy(new->entry->value, value, vsize);

    new->next = (struct ___mlf_kv_list *) object->attributes;
    object->attributes = (__mlf_kv_list *) new;
  } else {
    if ( old->entry->value_size == vsize ) {
      memcpy(old->entry->value, value, vsize);
    } else {
      char *t_value = (char *) realloc(old->entry->value, 
	      (size_t) ( vsize - old->entry->value_size ));
      if ( t_value == NULL ) {
	/* Empty for now, error flagging will be added here. */
      } else {
	memcpy(t_value, value, vsize);
	old->entry->value_size = vsize;
	old->entry->value = t_value;
      }
    }
  }

  return;
}

char *mlf_object_pop(mlf_object *object, char *key, int *vsize)
{
  __mlf_kv_list *iter, *ret;
  char *v_ptr;

  ret = NULL;
  v_ptr = NULL;

  iter = object->attributes;

  while ( iter != NULL ) {
    if ( iter->entry->key_size == strlen(key) ) {
      if ( strncmp(iter->entry->key, key, iter->entry->key_size) == 0 ) {
	ret = iter;
	break;
      } else {
	iter = (__mlf_kv_list *) iter->next;
      }
    } else {
      iter = (__mlf_kv_list *) iter->next;
    }
  }

  if (ret != NULL) {
    *vsize = ret->entry->value_size;
    v_ptr = (char *) malloc( sizeof(char) * *vsize );
    memcpy(v_ptr, ret->entry->value, *vsize);
  } else {
    *vsize = 0;
  }

  return v_ptr;
}

char *mlf_object_serialize(mlf_object *object, uint32_t *size)
{
  uint32_t total_size = 0;
  char *stream, *s_iter;
  __mlf_kv_list *iter = NULL;

  /* size of the type field */
  total_size += sizeof(uint8_t);

  iter = object->attributes;

  while (iter != NULL) {
    total_size = total_size + iter->entry->key_size + iter->entry->value_size;
    iter = (__mlf_kv_list *) iter->next;
  }

  *size = total_size;

  stream = (char *) malloc( sizeof(char) * total_size );
  s_iter = stream;

  memcpy(s_iter, &object->type, sizeof(uint8_t));
  s_iter = s_iter + sizeof(uint8_t);

  iter = object->attributes;

  while (iter != NULL) {
    memcpy(s_iter, &iter->entry->key_size, sizeof(uint32_t));
    s_iter = s_iter + sizeof(uint32_t);

    memcpy(s_iter, iter->entry->key, iter->entry->key_size);
    s_iter = s_iter + iter->entry->key_size;

    memcpy(s_iter, &iter->entry->value_size, sizeof(uint32_t));
    s_iter = s_iter + sizeof(uint32_t);

    memcpy(s_iter, iter->entry->value, iter->entry->value_size);
    s_iter = s_iter + iter->entry->value_size;

    iter = (__mlf_kv_list *) iter->next;
  }

  return stream;
}

mlf_object *mlf_object_deserialize(char *stream, int size)
{
  int counter = 0;
  char *s_iter, *t_key, *t_value;
  uint32_t t_size;
  mlf_object *object;

  object = (mlf_object *) malloc(sizeof(mlf_object));

  s_iter = stream;

  /* Pull the object type off the stream */
  memcpy(&object->type, s_iter, sizeof(uint8_t));
  s_iter = s_iter + sizeof(uint8_t);
  counter += sizeof(uint8_t);

  while ( counter < size ) {
    memcpy(&t_size, s_iter, sizeof(uint32_t));
    s_iter = s_iter + sizeof(uint32_t);

    memcpy(t_key, s_iter, t_size);
    s_iter = s_iter + t_size;

    memcpy(&t_size, s_iter, sizeof(uint32_t));
    s_iter = s_iter + sizeof(uint32_t);

    memcpy(t_value, s_iter, t_size);
    s_iter = s_iter + t_size;

    mlf_object_push(object, t_key, t_value, t_size);
  }

  return object;
}


